#include <unistd.h>
#include "queue/queue/queue.h"
#include "silksource.h"
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
}
#endif

using namespace std;


int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){
    int ret;
    int got_frame;
    AVPacket enc_pkt;
    if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &
          CODEC_CAP_DELAY))
        return 0;
    while (1) {
        enc_pkt.data = NULL;
        enc_pkt.size = 0;
        av_init_packet(&enc_pkt);
        ret = avcodec_encode_audio2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,
                                     NULL, &got_frame);
        av_frame_free(NULL);
        if (ret < 0)
            break;
        if (!got_frame){
            ret=0;
            break;
        }
        printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d\n",enc_pkt.size);
        /* mux encoded frame */
        ret = av_write_frame(fmt_ctx, &enc_pkt);
        if (ret < 0)
            break;
    }
    return ret;
}

Queue unlockQueue(2048*1024);

static void *decodeThread(void *parm)
{
    uint8_t frame[2048];
    FILE *fp = fopen("../jni/out.pcm", "wb");
    int size = 0;
    int totalsize = 0;
    //silk
    class SilkSource *silkSource = new SilkSource();
    if(silkSource->init((char *)parm)){
        printf("%s:init error\n", __FUNCTION__);
        return NULL;
    }
    while(!silkSource->decode_apacket(frame, &size)){
        //printf("%s:decode size=%d\n", __FUNCTION__, size);
        totalsize += size;
        unlockQueue.Put(frame, size);
        fwrite(frame, 1, size, fp);
    }
    fclose(fp);

    printf("=============total size = %d\n", totalsize);

}

static void *encodeThread(void *parm)
{
#if 1
    AVFormatContext *pFormatCtx;
    char *dst = (char *)parm;

    AVOutputFormat* fmt;
    AVStream* audio_st;
    AVCodecContext* pCodecCtx;
    AVCodec* pCodec;

    uint8_t* frame_buf;
    AVFrame* pFrame;
    AVPacket pkt;

    int got_frame=0;
    int ret=0;
    int size=0;
    int i = 0;

    FILE *dumppcm = fopen("../jni/in.pcm", "wb");

    av_register_all();

    //Method 1.
    pFormatCtx = avformat_alloc_context();
    fmt = av_guess_format(NULL, dst, NULL);
    pFormatCtx->oformat = fmt;

    //Method 2.
    //avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, dst);
    //fmt = pFormatCtx->oformat;

    //Open output URL
    if (avio_open(&pFormatCtx->pb, dst, AVIO_FLAG_READ_WRITE) < 0){
        printf("Failed to open output file!\n");
        return NULL;
    }

    audio_st = avformat_new_stream(pFormatCtx, 0);
    if (audio_st==NULL){
        return NULL;
    }
    pCodecCtx = audio_st->codec;
    pCodecCtx->codec_id = fmt->audio_codec;
    pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
    pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
    pCodecCtx->sample_rate= 8000;
    pCodecCtx->channel_layout=AV_CH_LAYOUT_STEREO;
    pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);
    pCodecCtx->bit_rate = 48000;

    //Show some information
    av_dump_format(pFormatCtx, 0, dst, 1);

    pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
    if (!pCodec){
        printf("Can not find encoder!\n");
        return NULL;
    }
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){
        printf("Failed to open encoder!\n");
        return NULL;
    }
    printf("get nb_samples = %d\n", pCodecCtx->frame_size);
    pFrame = av_frame_alloc();
    pFrame->nb_samples= pCodecCtx->frame_size;
    pFrame->format= pCodecCtx->sample_fmt;

    size = av_samples_get_buffer_size(NULL, pCodecCtx->channels,pCodecCtx->frame_size,pCodecCtx->sample_fmt, 1);
    printf("av_samples_get_buffer_size = %d\n", size);
    frame_buf = (uint8_t *)av_malloc(size);
    avcodec_fill_audio_frame(pFrame, pCodecCtx->channels, pCodecCtx->sample_fmt,(const uint8_t*)frame_buf, size, 1);

    //Write Header
    avformat_write_header(pFormatCtx,NULL);

    av_new_packet(&pkt,size);
    int retlen = 0;
    while((retlen = unlockQueue.Get(frame_buf, size)) > 0){
        //printf("get %d\n", size);
        fwrite(frame_buf, 1, retlen, dumppcm);

        pFrame->data[0] = frame_buf;  //PCM Data
        //pFrame->pts=(i++)*100;

        got_frame=0;
        //Encode
        ret = avcodec_encode_audio2(pCodecCtx, &pkt,pFrame, &got_frame);
        if(ret < 0){
            printf("Failed to encode!\n");
            return NULL;
        }
        if (got_frame==1){
            //printf("Succeed to encode 1 frame! \tsize:%5d\n",pkt.size);
            pkt.stream_index = audio_st->index;
            ret = av_write_frame(pFormatCtx, &pkt);
            av_free_packet(&pkt);
        }
        if(retlen < size){
            break;
        }
    }
    fclose(dumppcm);

    //Flush Encoder
    ret = flush_encoder(pFormatCtx,0);
    if (ret < 0) {
        printf("Flushing encoder failed\n");
        return NULL;
    }

    //Write Trailer
    av_write_trailer(pFormatCtx);

    //Clean
    if (audio_st){
        avcodec_close(audio_st->codec);
        av_free(pFrame);
        av_free(frame_buf);
    }
    avio_close(pFormatCtx->pb);
    avformat_free_context(pFormatCtx);
#endif
    return NULL;
}
//conv("./xx.amr", "yy.aac")
int conv(char *src, char *dst)
{
    pthread_t id_silkdecode;
    pthread_t id_encode;
    unlockQueue.Initialize();

    pthread_create(&id_silkdecode, NULL, decodeThread, src);
    pthread_create(&id_encode, NULL, encodeThread, dst);

    pthread_join(id_silkdecode, NULL);
    pthread_join(id_encode, NULL);

    return 0;
}

int main(int argc, char *argv[])
{
    int ret = 0;
    if(argc == 3){
        ret = conv(argv[1], argv[2]);
    }else{
        printf("%s <src file> <dest file>\n", argv[0]);
        //test
        conv("../jni/a.amr",
             "../jni/b.aac");
        ret = -1;
    }

    return ret;
}

